/****************************************************************************
 *
 * Copyright (C) 2003-2008 Los Alamos National Security, LLC
 *                         Packet Analytics Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.  You may not use, modify or
 * distribute this program under any other version of the GNU General
 * Public License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************/
package nfse;

import java.io.*;

import nfse.NetFSE;
import java.lang.Thread;
import java.net.Socket;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

/**
 * This class is launched by the server according to the system metadata's
 * syslog configuration. It tails a file and sends records that match to
 * NetListener for processing.
 * 
 * @author bduphoff
 * 
 */
public class SyslogThread extends Thread {

    /***************************************************************************
     * The sensor ID associated with this object. Each sensor can have at most
     * one Syslog configuration.
     */
    private int sensor = -1;

    /***************************************************************************
     * The keyword to look for in the syslog header to determine which logs to
     * parse and which to ignore.
     */
    private String keyword = "";

    /***************************************************************************
     * For log information with no IP address, a source IP using the IP
     * specified in this attribute will be appended to all incoming logs for
     * this instance. For example, all logs from a critical server could be
     * searched on by source IP by setting this value for a sensor.
     */
    private String ip = "";

    /***************************************************************************
     * The absolute file path that will be tailed by this instance. Typically
     * some file in /var/log.
     */
    private String filePath = "";

    /***************************************************************************
     * The process tailing the file in the filePath attribute.
     */
    private Process tailProcess = null;

    /***************************************************************************
     * Once this value is set, the thread will exit and stop processing logs for
     * this sensor.
     */
    private boolean quit = false;

    /***************************************************************************
     * Sets up necessary configuration for this instance.
     * 
     * @param sensor
     *            The sensor ID associated with this instance
     * @param filePath
     *            The absolute path of the file to process
     * @param keyword
     *            The keyword to search for in each log. Only logs with the
     *            keyword will be processed. Optional; "" to not specify a
     *            keyword.
     * @param ip
     *            The IP address to append as a source IP for this instance.
     *            Optional; "" to not specify an IP.
     */
    public SyslogThread(int sensor, String filePath, String keyword, String ip) {
        this.sensor = sensor;
        this.ip = ip;
        this.keyword = keyword;
        this.filePath = filePath;
    }

    public String toString() {
        return sensor + ", " + filePath + ", " + keyword + ", " + ip;
    }

    /***************************************************************************
     * Returns the sensor ID associated with this instance.
     * 
     * @return
     */
    public int getSensorID() {
        return sensor;
    }

    /***************************************************************************
     * Sets the quit flag which will cause the process to end.
     * 
     * @return The tail process monitoring the log file.
     */
    public Process quit() {
        try {
            quit = true;
            return tailProcess;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /***************************************************************************
     * This thread runs in a continuous loop until the quit flag is set by
     * calling the quit() method on the thread. The thread opens a tail of the
     * file specified in the filePath attribute. A connection to the DataServer
     * is initiated and log data is streamed to the server in real time as it is
     * processed.
     */
    public void run() {
        while (true) {
            try {
                Thread.sleep(5);
            } catch (Exception e) {

            }
            if (quit) {
                return;
            }
            try {

                // Tail the file starting at the end
                String cmd = "tail -n 0 -F " + filePath;
                // System.out.println(cmd);
                tailProcess = Runtime.getRuntime().exec(cmd);

                // Create a reader for the log file
                BufferedReader reader = new BufferedReader(new InputStreamReader(tailProcess.getInputStream()));

                SocketFactory sf = null;
                if (NetFSE.USE_SSL_DATA)
                    // Use SSL sockets
                    sf = SSLSocketFactory.getDefault();
                else
                    // Use a plaintext socket
                    sf = SocketFactory.getDefault();
                Socket sock = sf.createSocket("localhost", NetFSE.NET_LISTENER_SERVER_PORT);

                DataOutputStream dos = new DataOutputStream(sock.getOutputStream());
                DataInputStream dis = new DataInputStream(sock.getInputStream());
                dos.writeInt(sensor);
                int reply = dis.readInt();
                if (reply == -1) {
                    System.out.println("Sensor not supported (" + sensor + ").");
                    dos.close();
                    dis.close();
                    sock.close();
                    return;
                }

                SensorInfo sensorInfo = NetFSE.getSensorInfo(sensor);
                NetFSERecord rec = NetFSE.getTypeClass(sensorInfo.getToolID());
                boolean isSyslog = rec.isSyslog();

                int tagLength = keyword.length();

                String line = null;

                try {
                    line = reader.readLine();
                } catch (Exception e) {
                    if (quit)
                        return;
                }
                while (line != null) {
                    // System.out.println(line);
                    line = line.trim();
                    int tagIdx = line.indexOf(keyword);
                    boolean matches = false;
                    try {
                        int tagIdx2 = line.indexOf(" ", tagIdx + keyword.length());
                        String tag = line.substring(tagIdx, tagIdx2);
                        // System.out.println(tag);
                        if (tag.endsWith(":"))
                            matches = true;
                    } catch (Exception e) {
                        // e.printStackTrace();
                    }
                    if (matches) {
                        // System.out.println(line);
                        if (line.endsWith("\\n"))
                            line = line.substring(0, line.length() - 2);
                        if (isSyslog) {
                            try {
                                if (ip.length() > 0)
                                    line = "-1;" + ip + ";" + line;
                                else
                                    line = "-1;-1;" + line;
                                byte[] bytes = line.getBytes();
                                dos.writeInt(bytes.length);
                                dos.write(bytes);
                                // System.out.println(line);
                            } catch (Exception E) {

                            }
                        } else {
                            try {
                                line = line.substring(tagIdx + tagLength + 2);
                                byte[] bytes = line.getBytes();
                                dos.writeInt(bytes.length);
                                dos.write(bytes);
                                // System.out.println(line);
                            } catch (Exception E) {
                                E.printStackTrace(System.out);
                                System.out.flush();
                                if (quit)
                                    return;

                            }
                        }
                    }
                    try {
                        line = reader.readLine();
                    } catch (Exception e) {
                        line = null;
                    }
                }
                dos.close();
                dis.close();
                reader.close();
                try {
                    tailProcess.getInputStream().close();
                    tailProcess.getOutputStream().close();
                    tailProcess.destroy();
                } catch (NullPointerException npe) {
                    if (quit)
                        return;
                } catch (Exception e) {
                    e.printStackTrace(System.out);
                    System.out.flush();
                }
            } catch (Exception E) {
                try {
                    Thread.sleep(5000);
                } catch (Exception e) {

                }
            }
            if (quit)
                return;
        }
    }
}
